//-----------------------------------------------------------------------------
//
//Copyright(c) 2020, ThorsianWay Technologies Co, Ltd
//All rights reserved.
//
//IP Name       :   float_add
//File Name     :   float_add.v
//Module name   :   float add
//Full name     :   float number adder 
//
//Author        :   zha daolu
//Email         :   
//Data          :   2020/5/13
//Version       :   V1.0
//
//Abstract      :   
//                  
//Called  by    :   GPU
//
//Modification history
//-----------------------------------------------------
//1.00: intial version 
//1.01: add pipeline stall port
//
//-----------------------------------------------------------------------------

//-----------------------------
//DEFINE MACRO
//----------------------------- 
module float_add(
    input clk,
    input rst_n,
    input [31:0] a,
    input [31:0] b,
    input add_en,
    input busy,
    output [31:0] z,
    output valid
);

//decompose
  reg       [26:0] a_m;
  reg       [26:0] b_m;
  reg       [9:0] a_e;
  reg       [9:0] b_e;
  reg       a_s;
  reg       b_s;
  reg 		valid_ff1;
  
always@(posedge clk or negedge rst_n)
begin
	if(~rst_n)
	begin
        valid_ff1 <= 1'b0;
	end
	else if(~busy)
	begin
        valid_ff1 <= add_en;
	end
end

always@(posedge clk or negedge rst_n)
begin
	if(~rst_n)
	begin
        a_m <= 27'b0;
        b_m <= 27'b0;
        a_e <= 10'b0;
        b_e <= 10'b0;
        a_s <= 1'b0;
        b_s <= 1'b0;
	end
	else if(add_en && ~busy)
	begin
        a_m <= {1'b1,a[22 : 0], 2'd0};
        b_m <= {1'b1,b[22 : 0], 2'd0};
        a_e <= a[30 : 23] - 127;
        b_e <= b[30 : 23] - 127;
        a_s <= a[31];
        b_s <= b[31];
	end
end



//aligmnet
  reg       [26:0] a_m_align;
  reg       [26:0] b_m_align;
  reg       [9:0] a_e_align;
  reg       [9:0] b_e_align;
  reg       a_s_align;
  reg       b_s_align;
  reg 		valid_ff2;
  
always@(posedge clk or negedge rst_n)
begin
	if(~rst_n)
	begin
        valid_ff2 <= 1'b0;
	end
	else if(~busy)
	begin
        valid_ff2 <= valid_ff1;
	end
end

always@(posedge clk or negedge rst_n)
begin
	if(~rst_n)
	begin
        a_m_align <= 27'b0;
        b_m_align <= 27'b0;
        a_e_align <= 10'b0;
        b_e_align <= 10'b0;
        a_s_align <= 1'b0;
        b_s_align <= 1'b0;
	end
	else if(valid_ff1 && ~busy)
	begin
		if($signed(a_e) > $signed(b_e))
		begin       
			b_m_align <= b_m >> ($signed(a_e) - $signed(b_e));       
			b_e_align <= b_e + ($signed(a_e) - $signed(b_e));      
			b_s_align <= b_s;
			a_m_align <= a_m;     
			a_e_align <= a_e;   
			a_s_align <= a_s;
		end
		else if($signed(b_e) > $signed(a_e))
		begin       
			a_m_align <= a_m >> ($signed(b_e) - $signed(a_e));       
			a_e_align <= a_e + ($signed(b_e) - $signed(a_e));      
			a_s_align <= a_s;
			b_m_align <= b_m;     
			b_e_align <= b_e;   
			b_s_align <= b_s;
		end
		else
		begin
			a_m_align <= a_m;     
			a_e_align <= a_e;   
			a_s_align <= a_s;		
			b_m_align <= b_m;     
			b_e_align <= b_e;   
			b_s_align <= b_s;
		end
	end
end


//add
reg [27:0] sum;
reg z_s;
reg [9:0] z_e;
reg 		valid_ff3;
  
always@(posedge clk or negedge rst_n)
begin
	if(~rst_n)
	begin
        valid_ff3 <= 1'b0;
	end
	else if(~busy)
	begin
        valid_ff3 <= valid_ff2;
	end
end
always@(posedge clk or negedge rst_n)
begin
	if(~rst_n)
	begin
		sum <= 28'b0;
		z_s <= 1'b0;	
	end
	else if(valid_ff2 && ~busy)
	begin
		z_e <= a_e_align;
		if (a_s_align == b_s_align) 
		begin
			sum <= a_m_align + b_m_align;
			z_s <= a_s_align;
		end 
		else if (a_m_align >= b_m_align) 
		begin
			sum <= a_m_align - b_m_align;
			z_s  <= a_s_align;
		end 
		else 
		begin
			sum <= b_m_align - a_m_align;
			z_s  <= b_s_align;
		end
	end
end


//normlize
reg [22:0] z_m_nor;
reg [7:0] z_e_nor;
reg zs_nor;

wire [4:0] z_s_clz;

wire [26:0] z_m_shift = sum[26:0] << z_s_clz;

clz_27 u_clz(
.a(sum[26:0]),
.clz_o(z_s_clz)
);

reg 		valid_ff4;
  
always@(posedge clk or negedge rst_n)
begin
	if(~rst_n)
	begin
        valid_ff4 <= 1'b0;
	end
	else if(~busy)
	begin
        valid_ff4 <= valid_ff3;
	end
end

always@(posedge clk or negedge rst_n)
begin
	if(~rst_n)
	begin
		z_m_nor <= 23'b0;
		z_e_nor <= 1'b0;
		zs_nor 	<= 1'b0;	
	end
	else if(valid_ff3 && ~busy)
	begin
		zs_nor <= z_s;
		if(sum[27])
		begin
			z_m_nor <= sum[26:4];
			z_e_nor <= z_e + 1'b1 + 8'd128;
		end
		else
		begin
			z_e_nor <= z_e - z_s_clz + 8'd128;
			z_m_nor <= z_m_shift[25:3];
		end
	end
end

assign z = {zs_nor,z_e_nor,z_m_nor};
assign valid = valid_ff4 && ~busy;


endmodule
